﻿using System;
using System.Collections.Generic;
using cfg.grid;
using DoDoveFramework;
using UnityEngine;
using UnityEngine.Tilemaps;

namespace Script
{
    public class TileMapKit : Singleton<TileMapKit>
    {
        private Transform _tileMapRoot;
        private Tilemap _dataTilemap;
        private Bounds _tilemapWorldBounds; // 瓦片地图的世界坐标边界
        private Tilemap _displayTilemap;
        
        
        private Dictionary<string, int> _placeholderTile = new Dictionary<string, int>();
        private ComputeShader _perlinNoiseShader;
        private List<float> _gridWeightBound = new List<float>();


        // 生成的纹理
        private RenderTexture _noiseTexture;
        private Renderer _noiseTarget;

        private cfg.vector2 _mapSize;

        private int _seed;

        private int _pixelsPerTile = 16; // 1瓦片对应16像素

        //四个邻居的常量
        protected Vector3Int[] NEIGHBOURS = new Vector3Int[]
        {
            new Vector3Int(0, 0, 0),
            new Vector3Int(1, 0, 0),
            new Vector3Int(0, 1, 0),
            new Vector3Int(1, 1, 0)
        };

        public void InitTileMapKit()
        {
        }

        protected override void Initialize()
        {
            _tileMapRoot = GameObject.Find("TileMapRoot")?.transform;
            if (_tileMapRoot == null)
            {
                var go = new TileMapBuilder()
                    .CreateGrid()
                    .AddTileMap(new Vector3(0.0f, 0.0f, 2.0f))
                    .AddTileMap(new Vector3(-0.5f, -0.5f, 0.0f), "DisplayTilemap")
                    .AddNoiseTargetGo(new Vector3(0.0f, 0.0f, -2.0f))
                    .InitPlaceholder(_placeholderTile)
                    .InitGridWeight(_gridWeightBound)
                    .Build();
                _tileMapRoot = go.transform;
                _tileMapRoot.SetParent(GameBoot.Instance.transform);
                _tileMapRoot.name = "TileMapRoot";

                _tileMapRoot.transform.localPosition = Vector3.zero;

                //cache 子节点

                _dataTilemap = GameObject.Find("TileMapRoot/DataTilemap")?.transform.GetComponent<Tilemap>();

                _displayTilemap = GameObject.Find("TileMapRoot/DisplayTilemap")?.transform.GetComponent<Tilemap>();


                _noiseTarget = GameObject.Find("TileMapRoot/NoiseTarget")?.transform.GetComponent<MeshRenderer>();

                //加载computer shader
                _perlinNoiseShader = ResKit.Instance.GetResByResName<ComputeShader>("Grid/Map", "PerlinNoise");
            }


            //初始化完成之后的test 
            GenerateNoiseTexture(2, 40000);
        }

        //输入地图大小，随机种子 创建地图
        public void GenerateNoiseTexture(int mapIndex, int seed)
        {
            _seed = seed;
            //确定地图的大小
            if (mapIndex <= 0) _mapSize = GameConfigKit.Instance.gameConfig.TbDefine.SmallSize;
            else if (mapIndex == 1) _mapSize = GameConfigKit.Instance.gameConfig.TbDefine.MidSize;
            else _mapSize = GameConfigKit.Instance.gameConfig.TbDefine.BigSize;

            if (_noiseTexture != null)
                _noiseTexture.Release();

            _noiseTexture = new RenderTexture((int)_mapSize.X, (int)_mapSize.Y, 0, RenderTextureFormat.ARGB32);
            _noiseTexture.enableRandomWrite = true;
            _noiseTexture.Create();

            int kernelHandle = _perlinNoiseShader.FindKernel("CSMain");

            //shader参数赋值
            _perlinNoiseShader.SetTexture(kernelHandle, "Result", _noiseTexture);
            _perlinNoiseShader.SetInt("noiseWidth", (int)_mapSize.X);
            _perlinNoiseShader.SetInt("noiseHeight", (int)_mapSize.Y);


            _perlinNoiseShader.SetFloat("scale", GameConfigKit.Instance.gameConfig.TbDefine.NoiseScale);
            _perlinNoiseShader.SetInt("octaves", GameConfigKit.Instance.gameConfig.TbDefine.NoiseOctaves);
            _perlinNoiseShader.SetFloat("persistence", GameConfigKit.Instance.gameConfig.TbDefine.NoisePersistence);
            _perlinNoiseShader.SetFloat("lacunarity", GameConfigKit.Instance.gameConfig.TbDefine.NoiseLacunarity);
            _perlinNoiseShader.SetInt("seed", seed);

            // 计算线程组数量
            int threadGroupsX = Mathf.CeilToInt(_mapSize.X / 8.0f);
            int threadGroupsY = Mathf.CeilToInt(_mapSize.Y / 8.0f);
            // 执行Compute Shader
            _perlinNoiseShader.Dispatch(kernelHandle, threadGroupsX, threadGroupsY, 1);

            // 将生成的纹理应用到材质
            _noiseTarget.material.mainTexture = _noiseTexture;

            GenerateDataTilemap();

#if UNITY_EDITOR
            CreateNoiseSprite();
#endif
            //创建完地图设置大小
            CalculateTilemapWorldBounds();
        }


        private void CreateNoiseSprite()
        {
            Texture2D noiseTex2D = new Texture2D((int)_mapSize.X, (int)_mapSize.Y, TextureFormat.RGBA32, false);
            RenderTexture.active = _noiseTexture;
            noiseTex2D.ReadPixels(new Rect(0, 0, _noiseTexture.width, _noiseTexture.height), 0, 0);
            noiseTex2D.Apply();
            RenderTexture.active = null;


            Sprite noiseSprite = Sprite.Create(
                noiseTex2D,
                new Rect(0, 0, noiseTex2D.width, noiseTex2D.height),
                new Vector2(0.5f, 0.5f), // 中心点
                100.0f // Pixels Per Unit
            );


            GameObject spriteObject = new GameObject("NoiseSprite");
            SpriteRenderer renderer = spriteObject.AddComponent<SpriteRenderer>();
            renderer.sprite = noiseSprite;
        }

        //根据地图大小读取像素
        private void GenerateDataTilemap()
        {
            //清理上一次的瓦片
            _dataTilemap.ClearAllTiles();

            //读取texture
            int width = _noiseTexture.width;
            int height = _noiseTexture.height;

            // 创建临时Texture2D来读取RenderTexture数据
            Texture2D tempTexture = new Texture2D(width, height, TextureFormat.R8, false);

            // 读取RenderTexture数据
            RenderTexture.active = _noiseTexture;
            tempTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
            tempTexture.Apply();
            RenderTexture.active = null;

            // 获取所有像素的噪声值
            Color32[] pixels = tempTexture.GetPixels32();

            // 生成瓦片
            int tilemapWidth = Mathf.CeilToInt((float)width / _pixelsPerTile);
            int tilemapHeight = Mathf.CeilToInt((float)height / _pixelsPerTile);

            //中心瓦片
            Vector2Int tilemapCenter = new Vector2Int(
                (tilemapWidth - 1) / 2, // X方向中心瓦片索引
                (tilemapHeight - 1) / 2 // Y方向中心瓦片索引
            );

            for (int tileY = 0; tileY < tilemapHeight; tileY++)
            {
                for (int tileX = 0; tileX < tilemapWidth; tileX++)
                {
                    int startX = tileX * _pixelsPerTile;
                    int startY = tileY * _pixelsPerTile;
                    int endX = Mathf.Min(startX + _pixelsPerTile, width);
                    int endY = Mathf.Min(startY + _pixelsPerTile, height);
                    float tileNoiseValue = CalculateAverageNoise(
                        pixels, startX, startY, endX, endY, width);


                    Vector3Int centeredTilePos = new Vector3Int(
                        tileX - tilemapCenter.x, // X方向偏移：减去中心瓦片索引
                        tileY - tilemapCenter.y, // Y方向偏移：减去中心瓦片索引
                        0
                    );


                    SetDataTilemap(tileNoiseValue, centeredTilePos);
                }
            }


            // 清理临时资源
            UnityEngine.Object.DestroyImmediate(tempTexture);
        }


        private float CalculateAverageNoise(
            Color32[] pixels, int startX, int startY, int endX, int endY, int textureWidth)
        {
            float totalNoise = 0;
            int pixelCount = 0;

            // 遍历区域内所有像素（y先行优化内存访问）
            for (int y = startY; y < endY; y++)
            {
                for (int x = startX; x < endX; x++)
                {
                    int pixelIndex = y * textureWidth + x;
                    totalNoise += pixels[pixelIndex].r / 255.0f;
                    pixelCount++;
                }
            }

            // 防止除零（理论上pixelCount不会为0）
            return pixelCount > 0 ? totalNoise / pixelCount : 0;
        }

        private void SetDataTilemap(float noiseValue, Vector3Int tilePosition)
        {
            int index = _gridWeightBound.BinarySearch(noiseValue);


            if (index < 0)
            {
                index = ~index; // 取反得到插入点索引
            }

            // 区间索引 = 插入点索引 - 1
            index = Math.Max(0, index - 1);

            //找到对应的占位符资源
            var resName = GameConfigKit.Instance.gameConfig.TbDefine.PlaceholderRes[index];

            var tile = ResKit.Instance.GetResByResName<Tile>("Grid/PlaceHolder", resName);

            SetCell(tilePosition, tile);
        }

        //dataGrid 对应的displayGrid 
        public void SetCell(Vector3Int coords, Tile tile)
        {
            _dataTilemap.SetTile(coords, tile);
            SetDisplayTile(coords);
        }

        private void SetDisplayTile(Vector3Int coords)
        {
            for (int i = 0; i < NEIGHBOURS.Length; i++)
            {
                Vector3Int offsetCoords = coords + NEIGHBOURS[i];


                var gridMapper = CalculateDisplayTile(offsetCoords);

                if (gridMapper.IsAniTile != null && (bool)gridMapper.IsAniTile)
                {
                    _displayTilemap.SetTile(offsetCoords,
                        ResKit.Instance.GetResByResName<AnimatedTile>("Grid/Tile", gridMapper.Res));
                }
                else
                {
                    _displayTilemap.SetTile(offsetCoords,
                        ResKit.Instance.GetResByResName<Tile>("Grid/Tile", gridMapper.Res));
                }
            }
        }


        private gridMapper CalculateDisplayTile(Vector3Int coords)
        {
            var topRightOffsetCoords = coords - NEIGHBOURS[0];
            var topLeftOffsetCoords = coords - NEIGHBOURS[1];
            var botRightOffsetCoords = coords - NEIGHBOURS[2];
            var botLeftOffsetCoords = coords - NEIGHBOURS[3];

            var topRightTile = _dataTilemap.GetTile(topRightOffsetCoords);
            var topLeftTile = _dataTilemap.GetTile(topLeftOffsetCoords);
            var botRightTile = _dataTilemap.GetTile(botRightOffsetCoords);
            var botLeftTile = _dataTilemap.GetTile(botLeftOffsetCoords);

            var keyname1 = topLeftTile ? topLeftTile?.name : "TilePlaceholders_1";
            var keyname2 = topRightTile ? topRightTile.name : "TilePlaceholders_1";
            var keyname3 = botLeftTile ? botLeftTile.name : "TilePlaceholders_1";
            var keyname4 = botRightTile ? botRightTile.name : "TilePlaceholders_1";

            var key1 = _placeholderTile[keyname1];
            var key2 = _placeholderTile[keyname2];
            var key3 = _placeholderTile[keyname3];
            var key4 = _placeholderTile[keyname4];


            var gridMapper = GameConfigKit.Instance.gameConfig.TbMapper.Get(key1, key2, key3, key4);

            return gridMapper;
        }
        
        //获取 datamap的范围
        private void CalculateTilemapWorldBounds()
        {
            // 获取 Tilemap 的单元格边界（索引范围）
            BoundsInt cellBounds = _dataTilemap.cellBounds;
            // 获取单元格的世界大小（由 LayoutGrid 决定）
            Vector3 cellSize = _dataTilemap.layoutGrid.cellSize;
            // 获取 Tilemap 自身的世界位置
            Vector3 tilemapWorldPos = _dataTilemap.transform.position;

            // 计算世界坐标的最小/最大值
            Vector3 minWorldPos = new Vector3(
                cellBounds.min.x * cellSize.x + tilemapWorldPos.x,
                cellBounds.min.y * cellSize.y + tilemapWorldPos.y,
                tilemapWorldPos.z
            );
            Vector3 maxWorldPos = new Vector3(
                cellBounds.max.x * cellSize.x + tilemapWorldPos.x,
                cellBounds.max.y * cellSize.y + tilemapWorldPos.y,
                tilemapWorldPos.z
            );
            
            // 生成世界坐标边界
            _tilemapWorldBounds.SetMinMax(minWorldPos, maxWorldPos);
        }

        public Bounds GetTitlemapWorldBounds()
        {
            return _tilemapWorldBounds;
        }

        public Vector3 GetTileMapCellSize()
        {
            return _dataTilemap.layoutGrid.cellSize;
        }
    }
}